﻿using System;
using System.Windows.Input;
using Chambills.Present.Core.NotifyOnChange;
using Nito.MVVM;

namespace Present.Commands.Decorators
{
    /// <summary>
    /// Decorates other commands with specialised functionality
    /// </summary>
    public abstract class CommandDecorator<TInnerCommand> : NotifiableObject, ICommand
        where TInnerCommand : ICommand
    {

        private readonly CanExecuteChangedCore canExecuteChanged = new CanExecuteChangedCore();

        private TInnerCommand innerCommand;
        private readonly EventHandler innerCanExecuteChanged;

        public TInnerCommand InnerCommand
        {
            get { return innerCommand; }
            set
            {
                if (!Equals(innerCommand, null))
                    innerCommand.CanExecuteChanged -= this.innerCanExecuteChanged;

                innerCommand = value;
                
                if(!Equals(innerCommand,null))
                    innerCommand.CanExecuteChanged += this.innerCanExecuteChanged;

                RaiseCanExecuteChanged();
            }
        }


        protected CommandDecorator() { }

        protected CommandDecorator(TInnerCommand innerCommand)
        {
            innerCanExecuteChanged = (sender, e) => canExecuteChanged.OnCanExecuteChanged();
            InnerCommand = innerCommand;
        }

        public virtual void Execute(object parameter)
        {
            InnerCommand.Execute(parameter);
        }

        public virtual bool CanExecute(object parameter)
        {
            return Equals(InnerCommand, null) ? false : InnerCommand.CanExecute(parameter);
        }



        /// <summary>
        /// This is a weak event. Provides notification that the result of <see cref="CanExecute"/> may be different.
        /// </summary>
        public virtual event EventHandler CanExecuteChanged
        {
            add { this.canExecuteChanged.CanExecuteChanged += value; }
            remove { this.canExecuteChanged.CanExecuteChanged -= value; }
        }

        protected void RaiseCanExecuteChanged()
        {
            canExecuteChanged.OnCanExecuteChanged();
        }

        protected override void DisposeResources()
        {
            base.DisposeResources();
            canExecuteChanged.Dispose();
        }
    }
}